/*
 * File:   Rect.h
 * Author: RedEyedKiller
 *
 * Created on 22 Μάρτιος 2010, 7:30 μμ
 */

#ifndef _VECTOR2_H
#define	_VECTOR2_H

#include <cmath>
#include "Mathematics.h"

using namespace std;

namespace Math
{

const double PI = 3.141592653589793238462;
const double PI180 = (PI / 180.0f);

template <typename NumType >
class Vector2Templ
{
public:

    // <editor-fold defaultstate="collapsed" desc="Constructors">

    Vector2Templ():x(0),y(0)
    {
    }

    Vector2Templ(const Vector2Templ& v)
    {
        this->x = static_cast<NumType> (v.x);
        this->y = static_cast<NumType> (v.y);
    }

    Vector2Templ(NumType magnitude, int angle, bool clockwise)
    {
        //convert degrees to rad
        if (clockwise)
        {
            angle = 360 - angle;
        }
        double angleRad = angle * (3.141592653589793238462 / 180.0f);
        //calculate the trigonometric numbers we need
        float sine = sin(angleRad);
        float cosi = cos(angleRad);

        NumType tmpX, tmpY;
        tmpX = magnitude;
        tmpY = 0;
        x = static_cast<NumType> (cosi * tmpX - sine * tmpY);
        y = static_cast<NumType> (sine * tmpX + cosi * tmpY);
    }

    Vector2Templ(int x, int y)
    {
        this->x = static_cast<NumType> (x);
        this->y = static_cast<NumType> (y);
    }

    Vector2Templ(float x, float y)
    {
        this->x = static_cast<NumType> (x);
        this->y = static_cast<NumType> (y);
    }

    Vector2Templ(double x, double y)
    {
        this->x = static_cast<NumType> (x);
        this->y = static_cast<NumType> (y);
    }// </editor-fold>

    ~Vector2Templ()
    {
    }

    // <editor-fold defaultstate="collapsed" desc="Sety Methods">

    inline void Set(const Vector2Templ& v)
    {
        this->x = static_cast<NumType> (v.x);
        this->y = static_cast<NumType> (v.y);
    }

    inline void Set(int x, int y)
    {
        this->x = static_cast<NumType> (x);
        this->y = static_cast<NumType> (y);
    }

    inline void Set(float x, float y)
    {
        this->x = static_cast<NumType> (x);
        this->y = static_cast<NumType> (y);
    }

    inline void Set(double x, double y)
    {
        this->x = static_cast<NumType> (x);
        this->y = static_cast<NumType> (y);
    }

    inline void SetX(int x)
    {
        this->x = static_cast<NumType> (x);
    }

    inline void SetY(int y)
    {
        this->y = static_cast<NumType> (y);
    }

    inline void SetX(float x)
    {
        this->x = static_cast<NumType> (x);
    }

    inline void SetY(float y)
    {
        this->y = static_cast<NumType> (y);
    }

    inline void SetX(double x)
    {
        this->x = static_cast<NumType> (x);
    }

    inline void SetY(double y)
    {
        this->y = static_cast<NumType> (y);
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Gety Methods">

    inline NumType GetX() const
    {
        return x;
    }

    inline NumType GetY() const
    {
        return y;
    }// </editor-fold>

    /*move methods*/

    /* Move methods add two arithmetic values to
     * each vector element*/

    inline void Move(int mx, int my)
    {
        x += static_cast<NumType> (mx);
        y += static_cast<NumType> (my);
    }

    inline void Move(float mx, float my)
    {
        x += static_cast<NumType> (mx);
        y += static_cast<NumType> (my);
    }

    inline void Move(double mx, double my)
    {
        x += static_cast<NumType> (mx);
        y += static_cast<NumType> (my);
    }

    /*return true if both x nad y are 0*/
    inline bool IsNull() const
    {
        if (x == 0 && y == 0)
        {
            return true;
        }
        return false;
    }
    
    /**
     * Sets the first to elements of the specified array into x,y of this vector.
     * @param array
     */
    void ToArray(NumType* array)
    {
        array[0] = x;
        array[1] = y;
    }

    /*
     * Operators
     */

    // -- Boolean Operators -- //

    inline bool operator ==(const Vector2Templ& p) const
    {
        return ((((p.x - PRECISION) < x) && (x < (p.x + PRECISION))) &&
                (((p.y - PRECISION) < y) && (y < (p.y + PRECISION))));
    }

    inline bool operator !=(const Vector2Templ& p) const
    {
        return !((((p.x - PRECISION) < x) && (x < (p.x + PRECISION))) &&
                (((p.y - PRECISION) < y) && (y < (p.y + PRECISION))));
    }

    //negate the values of the vector

    inline const Vector2Templ operator -() const
    {
        return Vector2Templ(-x, -y);
    }
    // -- Asignment Operator -- //

    inline Vector2Templ & operator =(const Vector2Templ& v)
    {
        //Set(v);
        this->x = static_cast<NumType> (v.x);
        this->y = static_cast<NumType> (v.y);
        return *this;
    }

    void Set(NumType magnitude, int angle, bool clockwise)
    {
        if (clockwise)
        {
            angle = 360 - angle;
        }
        //convert degrees to rad
        double angleRad = angle * PI180;
        //calculate the trigonometric numbers we need
        float sine = sin(angleRad);
        float cosi = cos(angleRad);
        //calculate the position of the new vector
        //construct an d return it
        NumType tmpX, tmpY;
        tmpX = magnitude;
        tmpY = 0;
        x = static_cast<NumType> (cosi * tmpX - sine * tmpY);
        y = static_cast<NumType> (sine * tmpX + cosi * tmpY);
    }


    /*
     * Arithmetic Operators
     */

    // <editor-fold defaultstate="collapsed" desc="Basic Operators (+ - * / ) with vectors">

    /* get the element by element sum of this and a vector */
    inline Vector2Templ operator +(const Vector2Templ& v) const
    {
        return Vector2Templ(this->x + v.x, this->y + v.y);
    }

    /* get the element by element difference of this and a vector */
    inline Vector2Templ operator -(const Vector2Templ& v) const
    {
        return Vector2Templ(this->x - v.x, this->y - v.y);
    }

    /* get the element by element product of this and a vector */
    inline Vector2Templ operator *(const Vector2Templ& v) const
    {
        return Vector2Templ(this->x * v.x, this->y * v.y);
    }

    /* get the element by element division of this and a vector */
    inline Vector2Templ operator /(const Vector2Templ& v) const
    {
        return Vector2Templ(this->x / v.x, this->y / v.y);
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Basic Operators (+ - * / ) with ints">

    /* get the element by element sum of this and v */
    inline Vector2Templ operator +(int v) const
    {
        return Vector2Templ(this->x + v, this->y + v);
    }

    /* get the element by element difference of this and v */
    inline Vector2Templ operator -(int v) const
    {
        return Vector2Templ(this->x - v, this->y - v);
    }

    /* get the element by element product of this and v */
    inline Vector2Templ operator *(int v) const
    {
        return Vector2Templ(this->x * v, this->y * v);
    }

    /* get the element by element division of this and v */
    inline Vector2Templ operator /(int v) const
    {
        return Vector2Templ(this->x / v, this->y / v);
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Basic Operators (+ - * / ) with floats">

    /* get the element by element sum of this and v */
    inline Vector2Templ operator +(float v) const
    {
        return Vector2Templ(this->x + v, this->y + v);
    }

    /* get the element by element difference of this and v */
    inline Vector2Templ operator -(float v) const
    {
        return Vector2Templ(this->x - v, this->y - v);
    }

    /* get the element by element product of this and v */
    inline Vector2Templ operator *(float v) const
    {
        return Vector2Templ(this->x * v, this->y * v);
    }

    /* get the element by element division of this and v */
    inline Vector2Templ operator /(float v) const
    {
        return Vector2Templ(this->x / v, this->y / v);
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Basic Operators (+ - * / ) with doubles">

    /* get the element by element sum of this and v */
    inline Vector2Templ operator +(double v) const
    {
        return Vector2Templ(this->x + v, this->y + v);
    }

    /* get the element by element difference of this and v */
    inline Vector2Templ operator -(double v) const
    {
        return Vector2Templ(this->x - v, this->y - v);
    }

    /* get the element by element product of this and v */
    inline Vector2Templ operator *(double v) const
    {
        return Vector2Templ(this->x * v, this->y * v);
    }

    /* get the element by element division of this and v */
    inline Vector2Templ operator /(double v) const
    {
        return Vector2Templ(this->x / v, this->y / v);
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="x= operatos with vectors">

    /*adds each element of v to the coresponding element of this*/
    inline void operator+=(const Vector2Templ& v)
    {
        x += v.x;
        y += v.y;
    }

    /*substracts each element of v from the coresponding element of this*/
    inline void operator-=(const Vector2Templ& v)
    {
        x -= v.x;
        y -= v.y;
    }

    /*multiplies each element of v with each the coresponding of  this*/
    inline void operator*=(const Vector2Templ& v)
    {
        x *= v.x;
        y *= v.y;
    }

    /*divides each element of v with each the coresponding of this*/
    inline void operator/=(const Vector2Templ& v)
    {
        x /= v.x;
        y /= v.y;
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="x= operators with ints">

    /*adds value to each element of this*/
    inline void operator+=(const int& value)
    {
        x += value;
        y += value;
    }

    /*substract value from each element of this*/
    inline void operator-=(const int& value)
    {
        x -= value;
        y -= value;
    }

    /*multiply each element of this by value*/
    inline void operator*=(const int& mul)
    {
        x *= mul;
        y *= mul;
    }

    /*divide each element of this by value*/
    inline void operator/=(const int& mul)
    {
        x /= mul;
        y /= mul;
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="x= operators with floats">

    /*adds value to this*/
    inline void operator+=(const float& value)
    {
        x += value;
        y += value;
    }

    /*substract value from each element of this*/
    inline void operator-=(const float& value)
    {
        x -= value;
        y -= value;
    }

    /*multiply each element of this by value*/
    inline void operator*=(const float& mul)
    {
        x *= mul;
        y *= mul;
    }

    /*divide each element of this by value*/
    inline void operator/=(const float& div)
    {
        x /= div;
        y /= div;
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="x= operators with doubles">

    /*adds value to this*/
    inline void operator+=(const double& value)
    {
        x += value;
        y += value;
    }

    /*substract value from each element of this*/
    inline void operator-=(const double& value)
    {
        x -= value;
        y -= value;
    }

    /*multiply each element of this by value*/
    inline void operator*=(const double& mul)
    {
        x *= mul;
        y *= mul;
    }

    /*divide each element of this by value*/
    inline void operator/=(const double& mul)
    {
        x /= mul;
        y /= mul;
    }// </editor-fold>

    /* returns the direction factor by the factor ptr.
     * if x == 0 the factor is infinite and so this
     * method returns false*/
    bool GetDirectionFactor(float* factor)
    {
        if (x == 0) return false;
        *factor = y / x;
	return true;
    }

    /* if the 2 vectors have a 90o degrees angle this returns true*/
    bool ArePerpendicular(const Vector2Templ& vector) const
    {
        return (DotProduct(vector) == 0);
    }

    /* return true if this and vector have the same length(magnitude)
     * but opposite direction*/
    bool AreOpposite(const Vector2Templ& vector) const
    {
        return (x == -vector.x && y == -vector.y);
    }

    /* return true if this and vector are parellel*/
    bool AreParellel(const Vector2Templ& vector) const
    {
        return (CrossProduct(vector) == 0);
    }

    /*the length(magnitude) of the vector*/
    inline float Length() const
    {
        return static_cast<float> (sqrt(static_cast<float> (x * x + y * y)));
    }

    /* returns the length(magnitude) of this in square
     * it is used for optimazation*/
    inline NumType SquareLength() const
    {
        return x * x + y * y;
    }

    /*returns the square of the distance of this and v for optimization*/
    template<typename T>
    inline float DistanceSqr(const Vector2Templ<T>& vector) const
    {
        float x = vector.GetX() - this->x;
        float y = vector.GetY() - this->y;
        return static_cast<float> (x * x + y * y);
    }

    /*returns the distance of this and v*/
    template<typename T>
    inline float Distance(const Vector2Templ<T>& vector) const
    {
        register float x = static_cast<float> (vector.GetX() - this->x);
        register float y = static_cast<float> (vector.GetY() - this->y);
        return static_cast<float> (sqrt(x * x + y * y));
    }

    /*returns the cross product (det) of this and v*/
    inline float CrossProduct(const Vector2Templ& v) const
    {
        return static_cast<float> (x * v.y - y * v.x);
    }

    /*returns the dot product of this and v*/
    inline float DotProduct(const Vector2Templ& v) const
    {
        return static_cast<float> (x * v.x + y * v.y);
    }

    /*returns the angle between this and v in rads*/
    float AngleInRads(const Vector2Templ& v) const
    {
        return acos(Normal().DotProduct(v.Normal()));
    }

    /*returns the angle between this and v in degrees*/
    int AngleInDegrees(const Vector2Templ& v) const
    {
        return RadiansToDegrees(acos(Normal().DotProduct(v.Normal())));
    }

    /** 
     * set vector to its normal
     * the new vector will have the same direction
     * but length (magnitude) 1
     */
    void Normalize()
    {
        NumType length = Length();
        if (length != 0)
            this->Set(x / length, y / length);
        else
            this->Set(0, 0);
    }

    /*return the normal of the vector without changing the original*/
    Vector2Templ<float> Normal() const
    {
        NumType length = Length();
        if (length != 0)
            return Vector2Templ<float>(x / length, y / length);
        else
            return Vector2Templ<float>(0.0f, 0.0f);
    }

    /** 
     * Flips all the components of the vector. 
     */
    inline void Invert()
    {
        x = -x;
        y = -y;
    }

    inline void Flip()
    {
        int tmp = x;
        x = y;
        y = tmp;
    }

    /**
     * Sets this vector's member to their absolute value.
     */
    void Absolute()
    {
        x = std::abs(x);
        y = std::abs(y);
    }

    /**
     * Returns a vector ( abs(x) , abs(y) )
     * @return 
     */
    Vector2Templ GetAbsolute() const
    {
        return Vector2Templ(std::abs(x), std::abs(y));
    }

    /*
     * Returns the angle of this and the x axis in degrees.
     */
    int GetAngleInDegrees() const
    {
        NumType length = Length();
        if (length == 0)return 0;
        return static_cast<int> (std::acos(static_cast<float> (x / length)) / PI180);
    }

    /*
     * Returns the angle of this and the x axis in rads.
     */
    float GetAngleInRads() const
    {
        NumType length = Length();
        if (length == 0)return 0;
        return static_cast<float> (std::acos(static_cast<float> (x / length)));
    }

    /* converts this to a NewType Vector
     * ex. Vector2Templ<int> --> Vector2Templ<float>
     */
    template<typename NewType>
    Vector2Templ<NewType> ConvertTo() const
    {
        return Vector2Templ<NewType > (x, y);
    }

    /* returns a vector with the same length and start point as this
     * The returned vector and this create an angle of angleDeg degrees
     * in counter clockwise rotation*/
    Vector2Templ GetRotatedVector(int angleDeg) const
    {
        //convert degrees to rad
        double angleRad = angleDeg * PI180;
        //calculate the trigonometric numbers we need
        float sine = sin(angleRad);
        float cosi = cos(angleRad);
        //calculate the position of the new vector
        //construct an d return it
        return Vector2Templ<NumType > (cosi * x - sine * y, sine * x + cosi * y);
    }

    /**
     * returns a vector with the same length and start point as this
     * The returned vector and this create an angle of angleRads rads
     * in counter clockwise rotation
     */
    Vector2Templ GetRotatedVector(float angleRads) const
    {
        //calculate the trigonometric numbers we need
        float sine = sin(angleRads);
        float cosi = cos(angleRads);
        //calculate the position of the new vector
        //construct an d return it
        return Vector2Templ<NumType > (cosi * x - sine * y, sine * x + cosi * y);
    }

    /**
     * rotate this to the given angle in counter clockwise rotation
     */
    void RotateVector(int angleDeg)
    {
        //convert degrees to rad
        double angleRad = angleDeg * PI180;
        //calculate the trigonometric numbers we need
        double sine = sin(angleRad);
        double cosi = cos(angleRad);
        //calculate the new position of the vector
        NumType oldX = x;
        NumType oldY = y;
        x = static_cast<NumType> (cosi * oldX - sine * oldY);
        y = static_cast<NumType> (sine * oldX + cosi * oldY);
    }

    /**
     * rotate this to the given angle in counter clockwise rotation
     */
    void RotateVector(float angleRads)
    {
        //calculate the trigonometric numbers we need
        float sine = sin(angleRads);
        float cosi = cos(angleRads);
        //calculate the new position of the vector
        NumType oldX = x;
        NumType oldY = y;
        x = static_cast<NumType> (cosi * oldX - sine * oldY);
        y = static_cast<NumType> (sine * oldX + cosi * oldY);
    }

    /**
     *  return a vector which is the projection of this on vector.
     * The new vector will have the same direction and carier as the vect
     * and length equal to the product of this with the cosine of the angle
     * between this and vector
     */
    Vector2Templ<NumType> ProjectionOn(const Vector2Templ& vect) const
    {
        return vect.Normal() * this->DotProduct(vect);
    }

    /**
     * A Null vector (0,0) defined statically. (One for each type)
     */
    static Vector2Templ<NumType> Zero;
    static Vector2Templ<NumType> One;
    static Vector2Templ<NumType> Up;
    static Vector2Templ<NumType> Down;
    static Vector2Templ<NumType> Left;
    static Vector2Templ<NumType> Right;

private:
    NumType x, y; //the elements of vector
};

typedef Vector2Templ<int> Vector2I;
typedef Vector2Templ<float> Vector2F;
typedef Vector2Templ<double> Vector2D;
}

#endif	/* _VECTOR2_H */
